
 <!DOCTYPE HTML>
<html >
<head>
  <meta charset="UTF-8">
  
    <title>链码开发测试 | B3434lockchain</title>
    <meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
    
    <meta name="author" content="b3434lockchain">
    

    
    <meta name="description" content="1.环境搭建1.1.操作系统准备因为之前使用Windows系统搭建环境太麻烦了，所以这次使用了ubuntu18.04这个版本，具体怎么安装参考其他教程。 1.2.安装curl因为后面的步骤我们需要用到curl命令来下载安装脚本和其他文件，所以需要先安装curl。 $ sudo apt install curl 1.3.安装dockerdocker分为两个版本，社区版docker-ce和企业版doc">
<meta name="keywords" content="周报,卢家豪">
<meta property="og:type" content="article">
<meta property="og:title" content="链码开发测试">
<meta property="og:url" content="http://b3434lockchain.gitee.io/2018/10/21/lujiahao/lujiahao3/index.html">
<meta property="og:site_name" content="B3434lockchain">
<meta property="og:description" content="1.环境搭建1.1.操作系统准备因为之前使用Windows系统搭建环境太麻烦了，所以这次使用了ubuntu18.04这个版本，具体怎么安装参考其他教程。 1.2.安装curl因为后面的步骤我们需要用到curl命令来下载安装脚本和其他文件，所以需要先安装curl。 $ sudo apt install curl 1.3.安装dockerdocker分为两个版本，社区版docker-ce和企业版doc">
<meta property="og:locale" content="default">
<meta property="og:image" content="https://i.imgur.com/QftIlco.jpg">
<meta property="og:image" content="https://i.imgur.com/D7LQKsU.jpg">
<meta property="og:image" content="https://i.imgur.com/vlOZCqe.jpg">
<meta property="og:image" content="https://i.imgur.com/jKg8xZo.png">
<meta property="og:image" content="https://i.imgur.com/tMPFovb.png">
<meta property="og:image" content="https://i.imgur.com/4d9JVmR.png">
<meta property="og:image" content="https://i.imgur.com/KUeuDKl.jpg">
<meta property="og:image" content="https://i.imgur.com/XODqHH6.png">
<meta property="og:image" content="https://i.imgur.com/u1uUt6P.png">
<meta property="og:image" content="https://i.imgur.com/qASzYHq.png">
<meta property="og:image" content="https://i.imgur.com/8GfAU8l.png">
<meta property="og:image" content="https://i.imgur.com/gAZmrBY.png">
<meta property="og:image" content="https://i.imgur.com/S3F2Vrd.png">
<meta property="og:updated_time" content="2018-10-25T14:18:16.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="链码开发测试">
<meta name="twitter:description" content="1.环境搭建1.1.操作系统准备因为之前使用Windows系统搭建环境太麻烦了，所以这次使用了ubuntu18.04这个版本，具体怎么安装参考其他教程。 1.2.安装curl因为后面的步骤我们需要用到curl命令来下载安装脚本和其他文件，所以需要先安装curl。 $ sudo apt install curl 1.3.安装dockerdocker分为两个版本，社区版docker-ce和企业版doc">
<meta name="twitter:image" content="https://i.imgur.com/QftIlco.jpg">

    
    <link rel="alternative" href="/atom.xml" title="B3434lockchain" type="application/atom+xml">
    
    
    <link rel="icon" href="/img/favicon.ico">
    
    
    <link rel="apple-touch-icon" href="/img/jacman.jpg">
    <link rel="apple-touch-icon-precomposed" href="/img/jacman.jpg">
    
    <link rel="stylesheet" href="/css/style.css">
</head>

  <body>
    <header>
      
<div>
		
			<div id="imglogo">
				<a href="/"><img src="/img/logo.png" alt="B3434lockchain" title="B3434lockchain"/></a>
			</div>
			
			<div id="textlogo">
				<h1 class="site-name"><a href="/" title="B3434lockchain">B3434lockchain</a></h1>
				<h2 class="blog-motto"></h2>
			</div>
			<div class="navbar"><a class="navbutton navmobile" href="#" title="Menu">
			</a></div>
			<nav class="animated">
				<ul>
					<ul>
					 
						<li><a href="/">Home</a></li>
					
						<li><a href="/archives">Archives</a></li>
					
						<li><a href="/about">About</a></li>
					
					<li>
 					
						<form class="search" action="https://www.baidu.com" target="_blank">
							<label>Search</label>
						<input name="s" type="hidden" value=  ><input type="text" name="q" size="30" placeholder="Search"><br>
						</form>
					
					</li>
				</ul>
			</nav>			
</div>
    </header>
    <div id="container">
      <div id="main" class="post" itemscope itemprop="blogPost">
  
	<article itemprop="articleBody"> 
		<header class="article-info clearfix">
  <h1 itemprop="name">
    
      <a href="/2018/10/21/lujiahao/lujiahao3/" title="链码开发测试" itemprop="url">链码开发测试</a>
  </h1>
  <p class="article-author">By
       
		<a href="/about" title="b3434lockchain" target="_blank" itemprop="author">b3434lockchain</a>
		
  <p class="article-time">
    <time datetime="2018-10-21T14:30:00.000Z" itemprop="datePublished"> Published 2018-10-21</time>
    
  </p>
</header>
	<div class="article-content">
		
		<div id="toc" class="toc-article">
			<strong class="toc-title">Contents</strong>
		
			<ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-环境搭建"><span class="toc-number">1.</span> <span class="toc-text">1.环境搭建</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-1-操作系统准备"><span class="toc-number">1.1.</span> <span class="toc-text">1.1.操作系统准备</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-2-安装curl"><span class="toc-number">1.2.</span> <span class="toc-text">1.2.安装curl</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-3-安装docker"><span class="toc-number">1.3.</span> <span class="toc-text">1.3.安装docker</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-4-安装docker-compose"><span class="toc-number">1.4.</span> <span class="toc-text">1.4.安装docker-compose</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-5-安装Go"><span class="toc-number">1.5.</span> <span class="toc-text">1.5.安装Go</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-6-安装并检测git"><span class="toc-number">1.6.</span> <span class="toc-text">1.6.安装并检测git</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-7-下载fabric的引导脚本bootstrap-sh"><span class="toc-number">1.7.</span> <span class="toc-text">1.7.下载fabric的引导脚本bootstrap.sh</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-8-执行引导脚本bootstrap-sh"><span class="toc-number">1.8.</span> <span class="toc-text">1.8.执行引导脚本bootstrap.sh</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-9-添加环境变量"><span class="toc-number">1.9.</span> <span class="toc-text">1.9.添加环境变量</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-10-测试网络"><span class="toc-number">1.10.</span> <span class="toc-text">1.10.测试网络</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-11-错误处理"><span class="toc-number">1.11.</span> <span class="toc-text">1.11.错误处理</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-链码测试"><span class="toc-number">2.</span> <span class="toc-text">2.链码测试</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-什么是链码"><span class="toc-number">2.1.</span> <span class="toc-text">2.1.什么是链码</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-链码API"><span class="toc-number">2.2.</span> <span class="toc-text">2.2.链码API</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-简单资产链码"><span class="toc-number">2.3.</span> <span class="toc-text">2.3.简单资产链码</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-1-选择代码的位置"><span class="toc-number">2.3.1.</span> <span class="toc-text">2.3.1.选择代码的位置</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-2-准备工作"><span class="toc-number">2.3.2.</span> <span class="toc-text">2.3.2.准备工作</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-3-初始化链码"><span class="toc-number">2.3.3.</span> <span class="toc-text">2.3.3.初始化链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-4-调用链码"><span class="toc-number">2.3.4.</span> <span class="toc-text">2.3.4.调用链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-5-实现链码"><span class="toc-number">2.3.5.</span> <span class="toc-text">2.3.5.实现链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-6-集合代码"><span class="toc-number">2.3.6.</span> <span class="toc-text">2.3.6.集合代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-7-测试链码"><span class="toc-number">2.3.7.</span> <span class="toc-text">2.3.7.测试链码</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-7-1-终端一：开启网络"><span class="toc-number">2.3.7.1.</span> <span class="toc-text">2.3.7.1.终端一：开启网络</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-7-2-终端二：编译和运行链码"><span class="toc-number">2.3.7.2.</span> <span class="toc-text">2.3.7.2.终端二：编译和运行链码</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-7-3-终端三：使用链码"><span class="toc-number">2.3.7.3.</span> <span class="toc-text">2.3.7.3.终端三：使用链码</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-在first-network中测试链码"><span class="toc-number">3.</span> <span class="toc-text">3.在first-network中测试链码</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#3-1-术语介绍"><span class="toc-number">3.1.</span> <span class="toc-text">3.1.术语介绍</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-1-Channel"><span class="toc-number">3.1.1.</span> <span class="toc-text">3.1.1.Channel</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-2-Chaincode"><span class="toc-number">3.1.2.</span> <span class="toc-text">3.1.2.Chaincode</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-3-Ledger"><span class="toc-number">3.1.3.</span> <span class="toc-text">3.1.3.Ledger</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-4-network"><span class="toc-number">3.1.4.</span> <span class="toc-text">3.1.4.network</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-5-Fabric-CA"><span class="toc-number">3.1.5.</span> <span class="toc-text">3.1.5.Fabric CA</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-6-Peer"><span class="toc-number">3.1.6.</span> <span class="toc-text">3.1.6.Peer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-7-ordering-service"><span class="toc-number">3.1.7.</span> <span class="toc-text">3.1.7.ordering service</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-8-MSP"><span class="toc-number">3.1.8.</span> <span class="toc-text">3.1.8.MSP</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-2-链码开发"><span class="toc-number">3.2.</span> <span class="toc-text">3.2.链码开发</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-1-在GOPATH目录下git源码"><span class="toc-number">3.2.1.</span> <span class="toc-text">3.2.1.在GOPATH目录下git源码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-2-编写代码"><span class="toc-number">3.2.2.</span> <span class="toc-text">3.2.2.编写代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-3-拷贝链码到docker容器里面"><span class="toc-number">3.2.3.</span> <span class="toc-text">3.2.3.拷贝链码到docker容器里面</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-4-安装链码"><span class="toc-number">3.2.4.</span> <span class="toc-text">3.2.4.安装链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-5-实例化链码"><span class="toc-number">3.2.5.</span> <span class="toc-text">3.2.5.实例化链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-6-调用链码"><span class="toc-number">3.2.6.</span> <span class="toc-text">3.2.6.调用链码</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#总结"><span class="toc-number">4.</span> <span class="toc-text">总结</span></a></li></ol>
		
		</div>
		
		<h1 id="1-环境搭建"><a href="#1-环境搭建" class="headerlink" title="1.环境搭建"></a>1.环境搭建</h1><h2 id="1-1-操作系统准备"><a href="#1-1-操作系统准备" class="headerlink" title="1.1.操作系统准备"></a>1.1.操作系统准备</h2><p>因为之前使用Windows系统搭建环境太麻烦了，所以这次使用了ubuntu18.04这个版本，具体怎么安装参考其他教程。</p>
<h2 id="1-2-安装curl"><a href="#1-2-安装curl" class="headerlink" title="1.2.安装curl"></a>1.2.安装curl</h2><p>因为后面的步骤我们需要用到curl命令来下载安装脚本和其他文件，所以需要先安装curl。</p>
<pre><code>$ sudo apt install curl
</code></pre><h2 id="1-3-安装docker"><a href="#1-3-安装docker" class="headerlink" title="1.3.安装docker"></a>1.3.安装docker</h2><p>docker分为两个版本，社区版docker-ce和企业版docker-ee。作为初入门的小白，使用社区版进行联系就够了。在ubantu操作系统上安装docker-ce有三种方式：</p>
<p>1) 通过docker仓库安装</p>
<p>2) 下载.deb包安装</p>
<p>3) 通过安装脚本安装</p>
<p>这里我们使用第三种方式进行安装。</p>
<p>第一步：使用curl下载安装脚本</p>
<pre><code>$ curl -fsSL get.docker.com -o get-docker.sh

$ ls get*
get-docker.sh
</code></pre><p>第二步：执行脚本进行安装，这一步需要等待一段时间</p>
<pre><code>$ sudo sh get-docker.sh
</code></pre><p>第三步：设置成非root用户也能执行docker，需要将普通用户加入docker组，这里当前用户叫joyce_lu.</p>
<pre><code>$ sudo usermod -aG docker joyce_lu
</code></pre><p>第四步：执行hello-world镜像，验证docker是否安装成功。看到以下信息则说明安装成功了。</p>
<pre><code>$ sudo docker run hello-world
...... 
Hello from Docker!
This message shows that your installation appears to be working correctly.
.....
</code></pre><h2 id="1-4-安装docker-compose"><a href="#1-4-安装docker-compose" class="headerlink" title="1.4.安装docker-compose"></a>1.4.安装docker-compose</h2><p> docker-compose是docker 容器进行编排的工具，定义和运行多容器的应用，可以一条命令启动多个容器。HyperLedger Fabric需要用到docker-compose，所以我们提前安装。</p>
<p>第一步：下载docker-conpose到对应目录，可以到<a href="https://github.com/docker/compose/releases查看docker-compose的对应版本，这里的下载目录为/usr/local/bin/docker-compose" target="_blank" rel="noopener">https://github.com/docker/compose/releases查看docker-compose的对应版本，这里的下载目录为/usr/local/bin/docker-compose</a></p>
<pre><code>$ sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
</code></pre><p>第二步：允许其他用户执行compose相关命令</p>
<pre><code>$ sudo chmod +x /usr/local/bin/docker-compose
</code></pre><p>第三步：验证安装是否成功，出现如下信息则是安装成功</p>
<pre><code>$ docker-compose --version
docker-compose version 1.21.2, build a133471
</code></pre><h2 id="1-5-安装Go"><a href="#1-5-安装Go" class="headerlink" title="1.5.安装Go"></a>1.5.安装Go</h2><p>第一步：下载golang的tar包</p>
<pre><code>$ wget https://storage.googleapis.com/golang/go1.11.linux-amd64.tar.gz
</code></pre><p>如果命令执行失败，则需要到第三方镜像源下载golang的tar包，如<a href="https://studygolang.com/dl。用浏览器打开该网址，下载linux的tar包即可。" target="_blank" rel="noopener">https://studygolang.com/dl。用浏览器打开该网址，下载linux的tar包即可。</a></p>
<p>第二步：解压tar包到对应目录，这里目录为/usr/local</p>
<pre><code>$ cd (下载目录)
$ sudo tar -C /usr/local -xzf go1.11.linux-amd64.tar.gz
</code></pre><p>第三步：创建goProjects目录</p>
<pre><code>$ mkdir $HOME/goProjects
</code></pre><p>第四步：打开~./bashrc，配置环境变量</p>
<pre><code>$ sudo gedit ~./bashrc
</code></pre><p>增加下面的环境变量，保存退出</p>
<pre><code>export GOROOT=/usr/local/go
export GOPATH=$HOME/goProjects
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
</code></pre><p>使用如下命令让修改的环境变量立即生效</p>
<pre><code>$ source ~/.bashrc
</code></pre><p>第五步：检测Go是否安装成功</p>
<pre><code>$ go version
go version go1.11 linux/amd64
</code></pre><h2 id="1-6-安装并检测git"><a href="#1-6-安装并检测git" class="headerlink" title="1.6.安装并检测git"></a>1.6.安装并检测git</h2><pre><code>$ sudo apt install git
$ git version
</code></pre><h2 id="1-7-下载fabric的引导脚本bootstrap-sh"><a href="#1-7-下载fabric的引导脚本bootstrap-sh" class="headerlink" title="1.7.下载fabric的引导脚本bootstrap.sh"></a>1.7.下载fabric的引导脚本bootstrap.sh</h2><p>通过运行这个bootstrap.sh可以去安装fabric，并且会下载fabric-sample，而且还会摘取docker镜像。先创建要保存脚本的目录，这里创建的是hyperledger-fabric目录，然后用curl下载脚本。</p>
<pre><code>$ cd ~
$ mkdir hyperledger-fabric
$ cd hyperledger-fabric
$ curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/release-1.2/scripts/bootstrap.sh -o bootstrap.sh
$ ls
bootstrap.sh
</code></pre><h2 id="1-8-执行引导脚本bootstrap-sh"><a href="#1-8-执行引导脚本bootstrap-sh" class="headerlink" title="1.8.执行引导脚本bootstrap.sh"></a>1.8.执行引导脚本bootstrap.sh</h2><p>下载过程需要等待一段时间。</p>
<pre><code>$ cd ~/hyperledger-fabric/
$ chmod 755 bootstrap.sh
$ sudo ./bootstrap.sh 1.2.0 1.2.0 0.4.10
</code></pre><p>下载结束后，可以执行docker images查看已经下载的docker相关镜像。</p>
<pre><code>$ sudo docker images | grep hyperledger*
</code></pre><h2 id="1-9-添加环境变量"><a href="#1-9-添加环境变量" class="headerlink" title="1.9.添加环境变量"></a>1.9.添加环境变量</h2><p>打开~./bashrc。</p>
<pre><code>$ sudo gedit ~./bashrc
</code></pre><p>增加下面的环境变量，保存退出</p>
<pre><code>export PATH=$PATH:$GOROOT/bin:$GOPATH/bin:/home/joyce_lu/hyperledger-fabric/fabric-samples/bin
</code></pre><p>使用如下命令让修改的环境变量立即生效</p>
<pre><code>$ source ~/.bashrc
</code></pre><p>检验环境变量是否成功</p>
<pre><code>$ fabric-ca-client version
fabric-ca-client:
 Version: 1.2.0
 Go version: go1.11
 OS/Arch: linux/amd64
</code></pre><h2 id="1-10-测试网络"><a href="#1-10-测试网络" class="headerlink" title="1.10.测试网络"></a>1.10.测试网络</h2><p>第一步：执行./byfn.sh generate命令生成相应的文件</p>
<p>其中byfn.sh为启动这个网络的启动脚本，启动脚本中除建立一个包含4个节点和1个Order service的网络外，还会启动一个容器用来执行脚本在channel中加入节点，部署和初始化chaincode，以及在部署的chaincode上执行交易。默认channel名称为mychannel，脚本程序会给网络实例生成数字证书和密钥；生成genesis block用来启动ordering service；一些用来配置channel的配置交易。<br><img src="https://i.imgur.com/QftIlco.jpg" alt=""></p>
<p>第二步：执行./byfn.sh up启动网络</p>
<p>当我们看到START的时候，表示启动成功。执行结束，显示END<br><img src="https://i.imgur.com/D7LQKsU.jpg" alt=""><br><img src="https://i.imgur.com/vlOZCqe.jpg" alt=""></p>
<p>通过docker ps命令可以查看到节点的启动情况。</p>
<p>第三步：执行./byfn.sh down 命令来停止网络</p>
<h2 id="1-11-错误处理"><a href="#1-11-错误处理" class="headerlink" title="1.11.错误处理"></a>1.11.错误处理</h2><p>有时候执行./byfn.sh up启动网络时会出现以下错误<br><img src="https://i.imgur.com/jKg8xZo.png" alt=""><br><img src="https://i.imgur.com/tMPFovb.png" alt=""></p>
<p>这可能是上次启动网络的时候容器里面的内容并没有清理，造成冲突。</p>
<pre><code>$ docker ps -qa | xargs docker stop
$ docker ps -qa | xargs docker rm
</code></pre><p>可以通过以上两个命令将docker容器内容清理干净，重新启动网络即可。</p>
<h1 id="2-链码测试"><a href="#2-链码测试" class="headerlink" title="2.链码测试"></a>2.链码测试</h1><p>这小节测试内容是对应Hyperledger Fabric官方英文文档里面Chaincode for Developers一篇文章，介绍链码的开发。</p>
<h2 id="2-1-什么是链码"><a href="#2-1-什么是链码" class="headerlink" title="2.1.什么是链码"></a>2.1.什么是链码</h2><p>链码是一个用Go、Node.js编写的实现了规定接口的程序。链码在一个安全的Docker容器中运行，该容器与背书对等进程隔离。链码通过应用程序提交的事务来初始化和管理账本状态。链码通常处理由网络成员同意的业务逻辑，因此它类似于”智能合约”。可以调用链码来更新或者查询交易提案中的账本。考虑到适当的许可，一个链码可以调用另一个链码，无论是在同一通道还是在不同的通道中，来访问其状态。请注意，如果被调用的链码与调用的链码在不同的通道上，则只允许读取和查询。也就是说，不同通道上被调用的链码只是一个查询，它在随后的提交阶段不参与状态验证检查。</p>
<h2 id="2-2-链码API"><a href="#2-2-链码API" class="headerlink" title="2.2.链码API"></a>2.2.链码API</h2><p>每个链码程序都必须实现Chaincode接口，其方法被调用用于响应接收到的事务。特别是当链码接收实例化或升级事务时，将调用Init方法，以便链码可以执行任何必要的初始化，包括应用程序状态的初始化。调用Invoke方法是为了响应接收调用事务来处理事务提案。在链码”shim”所有API中，另外一个接口是ChaincodeStubInterface，用于访问和修改账本，并在链码之间进行调用。</p>
<h2 id="2-3-简单资产链码"><a href="#2-3-简单资产链码" class="headerlink" title="2.3.简单资产链码"></a>2.3.简单资产链码</h2><p>我们的应用程序是用来在账本上创建资产(键值对)的一个基本示例链码。</p>
<h3 id="2-3-1-选择代码的位置"><a href="#2-3-1-选择代码的位置" class="headerlink" title="2.3.1.选择代码的位置"></a>2.3.1.选择代码的位置</h3><p>在$GOPATH路径下创建子目录src，然后再创建子目录sacc，最后创建一个文件sacc.go，并准备编写程序。</p>
<pre><code>$ mkdir -p $GOPATH/src/sacc &amp;&amp; cd $GOPATH/src/sacc
$ touch sacc.go
$ sudo gedit sacc.go
</code></pre><h3 id="2-3-2-准备工作"><a href="#2-3-2-准备工作" class="headerlink" title="2.3.2.准备工作"></a>2.3.2.准备工作</h3><p>首先我们做一些准备工作，和每个链码一样，它实现了Chaincode接口，特别是Init和Invoke函数。因此，我们需要使用Go import语句来导入链码的必要依赖库，我们将导入链码的shim包和peer protobuf包。接下来，我们添加一个结构SimpleAsset作为链码函数的接收器。</p>
<pre><code>package main

import (
    &quot;fmt&quot;

    &quot;github.com/hyperledger/fabric/core/chaincode/shim&quot;
    &quot;github.com/hyperledger/fabric/protos/peer&quot;
)

// SimpleAsset实现简单的链式代码来管理资产
type SimpleAsset struct {
}
</code></pre><h3 id="2-3-3-初始化链码"><a href="#2-3-3-初始化链码" class="headerlink" title="2.3.3.初始化链码"></a>2.3.3.初始化链码</h3><p>下一步，我们实现Init函数。</p>
<pre><code>// 在链码初始化过程中调用Init来初始化任何数据
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {

}
</code></pre><p>注意：链码升级时也会调用此函数，所以在编写要升级的现有链码的代码时，请确保适当地修改Init函数。特别是如果没有“迁移”或者没有任何内容作为升级的一部分进行初始化，请提供一个空的Init方法。</p>
<p>接下来，我们将使用ChaincodeStubInterface.GetStringArgs函数检索 Init 调用的参数并检查合法性。在我们的例子中，应当接收的是一个键值对。</p>
<pre><code>// 在链码初始化过程中调用Init来初始化任何数据。
// 请注意，链码升级也会调用此函数来重置或迁移数据。
// 因此，要小心避免无意中破坏账本数据的情况！
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
  // 从交易提案中获取参数
  args := stub.GetStringArgs()
  if len(args) != 2 {
    return shim.Error(&quot;Incorrect arguments. Expecting a key and a value&quot;)
  }
}
</code></pre><p>接下来，我们已经确保该调用时有效的，我们将把初始化状态存储到账本中。要做到这一点，我们将调用ChaincodeStubInterface.PutState并以键值对为参数传入。假设一切顺利，返回一个初始化成功的peer.Response对象。</p>
<pre><code>// 在链码初始化过程中调用Init来初始化任何数据。
// 请注意，链码升级也会调用此函数来重置或迁移数据。
// 因此，要小心避免无意中破坏账本数据的情况！
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
  // 从交易提案中获取参数
  args := stub.GetStringArgs()
  if len(args) != 2 {
    return shim.Error(&quot;Incorrect arguments. Expecting a key and a value&quot;)
  }

  // 在这里，通过调用stub.PutState()来设置任何变量或者资产

  // 我们在账本中存储key和value
  err := stub.PutState(args[0], []byte(args[1]))
  if err != nil {
    return shim.Error(fmt.Sprintf(&quot;Failed to create asset: %s&quot;, args[0]))
  }
  return shim.Success(nil)
}
</code></pre><h3 id="2-3-4-调用链码"><a href="#2-3-4-调用链码" class="headerlink" title="2.3.4.调用链码"></a>2.3.4.调用链码</h3><p>首先，我们添加Invoke函数的签名。</p>
<pre><code>// 在链码每个事务中，Invoke会被调用。
// 每个事务都是由Invoke函数创建的资产，要么是&apos;get&apos;要么是&apos;set&apos;。 
// Set方法可以通过指定新的键值对来创建新的资产。
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {

}
</code></pre><p>和上面的 Init 函数一样，我们需要从ChaincodeStubInterface中提取参数。 Invoke 函数的参数将会是要调用的链码应用程序中函数的名称。在我们的例子中，我们的应用程序只有两个参数：set和get，它们允许设置资产的值或检索当前状态。我们首先调用ChaincodeStubInterface.GetFunctionAndParameters来提取链码应用函数名称和参数。</p>
<pre><code>// 在链码每个事务中，Invoke会被调用。
// 每个事务都是由Invoke函数创建的资产，要么是&apos;get&apos;要么是&apos;set&apos;。 
// Set方法可以通过指定新的键值对来创建新的资产。
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // 从交易提案中提取函数和参数
    fn, args := stub.GetFunctionAndParameters()

}
</code></pre><p>接下来，我们将验证函数名称为set或get，并调用这些链码的应用函数。通过 shim.Success 或者 shim.Error 函数返回适当的响应，将响应序列化到gRPC protobuf消息中。</p>
<pre><code>// 在链码每个事务中，Invoke会被调用。
// 每个事务都是由Invoke函数创建的资产，要么是&apos;get&apos;要么是&apos;set&apos;。 
// Set方法可以通过指定新的键值对来创建新的资产。
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // 从交易提案中提取函数和参数
    fn, args := stub.GetFunctionAndParameters()

    var result string
    var err error
    if fn == &quot;set&quot; {
       result, err = set(stub, args)
    } else {
       result, err = get(stub, args)
    }
    if err != nil {
       return shim.Error(err.Error())
    }

    // 将结果作为成功负载返回
    return shim.Success([]byte(result))
}
</code></pre><h3 id="2-3-5-实现链码"><a href="#2-3-5-实现链码" class="headerlink" title="2.3.5.实现链码"></a>2.3.5.实现链码</h3><p>如上所述，我们的链码应用程序实现了两个可以通过 Invoke函数调用的函数。现在我们来实现这些功能。请注意，正如我们上面提到的那样，为了访问账本的状态，我们将利用链码的shim API的ChaincodeStubInterface.PutState和ChaincodeStubInterface.GetState函数。</p>
<pre><code>// 设置在账本上存储的资产（包括key和value）
// 如果key存在，它将覆盖新值
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 2 {
        return &quot;&quot;, fmt.Errorf(&quot;Incorrect arguments. Expecting a key and a value&quot;)
    }

    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil {
        return &quot;&quot;, fmt.Errorf(&quot;Failed to set asset: %s&quot;, args[0])
    }
    return args[1], nil
}

// 获取返回指定资产key的value值
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 1 {
        return &quot;&quot;, fmt.Errorf(&quot;Incorrect arguments. Expecting a key&quot;)
    }

    value, err := stub.GetState(args[0])
    if err != nil {
        return &quot;&quot;, fmt.Errorf(&quot;Failed to get asset: %s with error: %s&quot;, args[0], err)
    }
    if value == nil {
        return &quot;&quot;, fmt.Errorf(&quot;Asset not found: %s&quot;, args[0])
    }
    return string(value), nil
}
</code></pre><h3 id="2-3-6-集合代码"><a href="#2-3-6-集合代码" class="headerlink" title="2.3.6.集合代码"></a>2.3.6.集合代码</h3><p>最后，我们需要添加 main 函数，它将调用shim.Start函数。这是整个链码程序的完整源代码。</p>
<pre><code>package main

import (
    &quot;fmt&quot;

    &quot;github.com/hyperledger/fabric/core/chaincode/shim&quot;
    &quot;github.com/hyperledger/fabric/protos/peer&quot;
)

// SimpleAsset实现简单的链式代码来管理资产
type SimpleAsset struct {
}

// 在链码初始化过程中调用Init来初始化任何数据。
// 请注意，链码升级也会调用此函数来重置或迁移数据。
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
    // 从交易提案中获取参数
    args := stub.GetStringArgs()
    if len(args) != 2 {
        return shim.Error(&quot;Incorrect arguments. Expecting a key and a value&quot;)
    }

    // 在这里，通过调用stub.PutState()来设置任何变量或者资产

    // 我们在账本中存储key和value
    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil {
         return shim.Error(fmt.Sprintf(&quot;Failed to create asset: %s&quot;, args[0]))
    }
    return shim.Success(nil)
}

// 在链码每个事务中，Invoke会被调用。
// 每个事务都是由Init函数创建的资产，要么是&apos;get&apos;要么是&apos;set&apos;。 
// Set方法可以通过指定新的键值对来创建新的资产。
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // 从交易提案中提取函数和参数
    fn, args := stub.GetFunctionAndParameters()

    var result string
    var err error
    if fn == &quot;set&quot; {
        result, err = set(stub, args)
    } else { // fn为nil时，假设为get
           result, err = get(stub, args)
    }
    if err != nil {
         return shim.Error(err.Error())
    }

    // 将结果作为成功负载返回
    return shim.Success([]byte(result))
}

// 设置在账本上存储的资产（包括key和value）
// 如果key存在，它将覆盖新值
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 2 {
        return &quot;&quot;, fmt.Errorf(&quot;Incorrect arguments. Expecting a key and a value&quot;)
    }

    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil {
         return &quot;&quot;, fmt.Errorf(&quot;Failed to set asset: %s&quot;, args[0])
    }
    return args[1], nil
}

// 获取返回指定资产key的value值
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 1 {
        return &quot;&quot;, fmt.Errorf(&quot;Incorrect arguments. Expecting a key&quot;)
    }

    value, err := stub.GetState(args[0])
    if err != nil {
        return &quot;&quot;, fmt.Errorf(&quot;Failed to get asset: %s with error: %s&quot;, args[0], err)
    }
    if value == nil {
        return &quot;&quot;, fmt.Errorf(&quot;Asset not found: %s&quot;, args[0])
    }
    return string(value), nil
}

// main函数在实例化时启动容器中的链码
func main() {
    if err := shim.Start(new(SimpleAsset)); err != nil {
        fmt.Printf(&quot;Error starting SimpleAsset chaincode: %s&quot;, err)
    }
}
</code></pre><h3 id="2-3-7-测试链码"><a href="#2-3-7-测试链码" class="headerlink" title="2.3.7.测试链码"></a>2.3.7.测试链码</h3><p>开启三个终端，并且都进入到fabric-samples下的chaincode-docker-devmode目录下。</p>
<h4 id="2-3-7-1-终端一：开启网络"><a href="#2-3-7-1-终端一：开启网络" class="headerlink" title="2.3.7.1.终端一：开启网络"></a>2.3.7.1.终端一：开启网络</h4><pre><code>$ docker-compose -f docker-compose-simple.yaml up
</code></pre><p>以上命令使用 SingleSampleMSPSole 配置启动 orderer并且以”开发模式”启动节点peer。它还启动了两个额外的容器，一个用于链码环境，一个用于与链码交互的CLI。创建和加入通道的命令被嵌入到CLI容器中， 因此我们可以立即跳转到链码的调用。</p>
<p>运行结果如下：<br><img src="https://i.imgur.com/4d9JVmR.png" alt=""></p>
<h4 id="2-3-7-2-终端二：编译和运行链码"><a href="#2-3-7-2-终端二：编译和运行链码" class="headerlink" title="2.3.7.2.终端二：编译和运行链码"></a>2.3.7.2.终端二：编译和运行链码</h4><pre><code>$ docker exec -it chaincode bash
$ cd sacc
$ go build
$ CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc
</code></pre><p>运行结果如下：<br><img src="https://i.imgur.com/KUeuDKl.jpg" alt=""></p>
<h4 id="2-3-7-3-终端三：使用链码"><a href="#2-3-7-3-终端三：使用链码" class="headerlink" title="2.3.7.3.终端三：使用链码"></a>2.3.7.3.终端三：使用链码</h4><p>我们将利用CLI容器来驱动这些调用：</p>
<pre><code>$ docker exec -it cli bash
$ peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
</code></pre><p>运行结果如下：<br><img src="https://i.imgur.com/XODqHH6.png" alt=""></p>
<pre><code>$ peer chaincode instantiate -n mycc -v 0 -c &apos;{&quot;Args&quot;:[&quot;a&quot;,&quot;10&quot;]}&apos; -C myc
</code></pre><p>运行结果如下：<br><img src="https://i.imgur.com/u1uUt6P.png" alt=""></p>
<pre><code>$ peer chaincode invoke -n mycc -c &apos;{&quot;Args&quot;:[&quot;set&quot;, &quot;a&quot;, &quot;20&quot;]}&apos; -C myc
</code></pre><p>运行结果如下：<br><img src="https://i.imgur.com/qASzYHq.png" alt=""><br><img src="https://i.imgur.com/8GfAU8l.png" alt=""></p>
<pre><code>$ peer chaincode query -n mycc -c &apos;{&quot;Args&quot;:[&quot;query&quot;,&quot;a&quot;]}&apos; -C myc
</code></pre><p>运行结果如下：<br><img src="https://i.imgur.com/gAZmrBY.png" alt=""><br><img src="https://i.imgur.com/S3F2Vrd.png" alt=""></p>
<h1 id="3-在first-network中测试链码"><a href="#3-在first-network中测试链码" class="headerlink" title="3.在first-network中测试链码"></a>3.在first-network中测试链码</h1><h2 id="3-1-术语介绍"><a href="#3-1-术语介绍" class="headerlink" title="3.1.术语介绍"></a>3.1.术语介绍</h2><h3 id="3-1-1-Channel"><a href="#3-1-1-Channel" class="headerlink" title="3.1.1.Channel"></a>3.1.1.Channel</h3><p>数据通道，可以理解成独立的hyperledger fabric的实例。不同的Channel的数据彼此完全隔离，而且Channel可以保证区块链上的数据的隐私问题。</p>
<h3 id="3-1-2-Chaincode"><a href="#3-1-2-Chaincode" class="headerlink" title="3.1.2.Chaincode"></a>3.1.2.Chaincode</h3><p>链码，类似于智能合约。Chaincode定义了交易逻辑，而且ledger中的变化只能通过调用Chaincode来完成。</p>
<h3 id="3-1-3-Ledger"><a href="#3-1-3-Ledger" class="headerlink" title="3.1.3.Ledger"></a>3.1.3.Ledger</h3><p>账本，这是用于记录当前的世界状态，即数据，Ledger还链式的记录了所有的历史世界状态。在hyperledger里面，ledger是一个具有授权管理的共享帐本系统。从底层设计上保证了数据的一致性、有效性和不可篡改性。</p>
<h3 id="3-1-4-network"><a href="#3-1-4-network" class="headerlink" title="3.1.4.network"></a>3.1.4.network</h3><p>区块链网络，network是通过peer(节点)组成的，在同一个network中的peer实时同步记账，保证了ledger数据的一致性。</p>
<h3 id="3-1-5-Fabric-CA"><a href="#3-1-5-Fabric-CA" class="headerlink" title="3.1.5.Fabric CA"></a>3.1.5.Fabric CA</h3><p>Fabric CA是Hyperledger Fabric的证书颁发机构，它提供的功能有：身份的注册，或连接到LDAP作为用户注册表; 发放注册证书（ECerts）; 发布交易证书（TCerts），在交易时提供匿名性和不可链接性; 证书更新和撤销。</p>
<h3 id="3-1-6-Peer"><a href="#3-1-6-Peer" class="headerlink" title="3.1.6.Peer"></a>3.1.6.Peer</h3><p>区块链网络主要是由一系列peer节点的集合组成。Peer节点是区块链网络的基本，因为他们负责账本和智能合约。</p>
<h3 id="3-1-7-ordering-service"><a href="#3-1-7-ordering-service" class="headerlink" title="3.1.7.ordering service"></a>3.1.7.ordering service</h3><h3 id="3-1-8-MSP"><a href="#3-1-8-MSP" class="headerlink" title="3.1.8.MSP"></a>3.1.8.MSP</h3><h2 id="3-2-链码开发"><a href="#3-2-链码开发" class="headerlink" title="3.2.链码开发"></a>3.2.链码开发</h2><h3 id="3-2-1-在GOPATH目录下git源码"><a href="#3-2-1-在GOPATH目录下git源码" class="headerlink" title="3.2.1.在GOPATH目录下git源码"></a>3.2.1.在GOPATH目录下git源码</h3><p>在GOPATH目录下，下载源码，因为写链码需要用到里面的包。</p>
<pre><code>$ git clone http://gerrit.hyperledger.org/r/fabric
$ git clone http://gerrit.hyperledger.org/r/fabric-ca
</code></pre><h3 id="3-2-2-编写代码"><a href="#3-2-2-编写代码" class="headerlink" title="3.2.2.编写代码"></a>3.2.2.编写代码</h3><p>在GOPATH目录下新建一个目录，在目录中新建一个go文件，写入代码。</p>
<h3 id="3-2-3-拷贝链码到docker容器里面"><a href="#3-2-3-拷贝链码到docker容器里面" class="headerlink" title="3.2.3.拷贝链码到docker容器里面"></a>3.2.3.拷贝链码到docker容器里面</h3><p>注意：链码是必须放在容器的/opt/gopath/src目录下，也就是GOPATH目录下。</p>
<p>在GOPATH路径下使用docker cp命令，将链码代码从本地拷贝到容器中。</p>
<pre><code>$ docker cp ./test/test.go cli:/opt/gopath/src/github.com/chaincode/test/
</code></pre><h3 id="3-2-4-安装链码"><a href="#3-2-4-安装链码" class="headerlink" title="3.2.4.安装链码"></a>3.2.4.安装链码</h3><p>启动first-network网络，然后进入docker容器，进而安装链码。</p>
<pre><code>$ docker exec -it cli bash
$ peer chaincode install -n lghello -v 1.0 -p github.com/chaincode/test/
</code></pre><p>解释下参数说明：</p>
<p>-n:链码名字                                                                                                                                                                                                                                                                                                                                                     </p>
<p>-v:版本</p>
<p>-p:链码目录，定位到链码目录下面</p>
<h3 id="3-2-5-实例化链码"><a href="#3-2-5-实例化链码" class="headerlink" title="3.2.5.实例化链码"></a>3.2.5.实例化链码</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n lghello -v 1.0 -c &apos;&#123;&quot;Args&quot;:[&quot;init&quot;,&quot;a&quot;,&quot;100&quot;,&quot;b&quot;,&quot;200&quot;]&#125;&apos; -P &apos;OR (&apos;\&apos;&apos;Org1MSP.peer&apos;\&apos;&apos;,&apos;\&apos;&apos;Org2MSP.peer&apos;\&apos;&apos;)&apos;</span><br></pre></td></tr></table></figure>
<p>解释下参数说明：</p>
<p>-n:链码名字</p>
<p>-v:版本</p>
<p>-p:链码目录，定位到链码目录下面</p>
<p>–tls:是否启用tls证书</p>
<p>–cafile:如果启用tls证书，这参数是ca的地址</p>
<p>-P:这是背书方案，背书可以理解是执行链码成功的策略，要那几个节点同意后，链码就执行生效</p>
<p>-o:排序节点，要指定到端口</p>
<p>-C:初始化参数</p>
<h3 id="3-2-6-调用链码"><a href="#3-2-6-调用链码" class="headerlink" title="3.2.6.调用链码"></a>3.2.6.调用链码</h3><p>查询和转账命令，注意转账需要传对应的排序节点和tls证书。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ peer chaincode query -C mychannel -n lghello -c &apos;&#123;&quot;Args&quot;:[&quot;query&quot;,&quot;a&quot;]&#125;&apos;</span><br><span class="line">$ peer chaincode invoke -C mychannel -n lghello -c &apos;&#123;&quot;Args&quot;:[&quot;invoke&quot;,&quot;a&quot;,&quot;b&quot;,&quot;10&quot;]&#125;&apos; -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem</span><br></pre></td></tr></table></figure></p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>这次的周报的第一节部分是之前补充构建first-network网络的，比较顺利完成了。然后第二节部分是官网中Chaincode for Developers这篇文章的内容，主要介绍链码的开发，但是并不是在first-network网络中部署链码的，而是在chaincode-docker-devmode中进行部署测试的。因为一开始的时候两种网络应该有共同之处，所以想先熟悉一下链码的开发流程再直接转为在first-network中部署，结果出错了。可能是因为对链码开发的底层调用并没有完全了解，所以并没有完全顺利的在first-network中部署成功，但是对Golang开发智能合约有了一定的了解。第三节的内容是在first-network中部署链码的步骤，但是由于时间问题还没有完全跑通。</p>
  
	</div>
		<footer class="article-footer clearfix">
<div class="article-catetags">


  <div class="article-tags">
  
  <span></span> <a href="/tags/周报/">周报</a><a href="/tags/卢家豪/">卢家豪</a>
  </div>

</div>



	<div class="article-share" id="share">
	
	  <div data-url="http://b3434lockchain.gitee.io/2018/10/21/lujiahao/lujiahao3/" data-title="链码开发测试 | B3434lockchain" data-tsina="" class="share clearfix">
	  </div>
	
	</div>


</footer>

   	       
	</article>
	
<nav class="article-nav clearfix">
 
 <div class="prev" >
 <a href="/2018/10/25/fenghuawen/fenghuawen3/" title="冯华文 - 周报3">
  <strong>上一篇：</strong><br/>
  <span>
  冯华文 - 周报3</span>
</a>
</div>


<div class="next">
<a href="/2018/10/21/susijie/susijie3/"  title="第三周周报">
 <strong>下一篇：</strong><br/> 
 <span>第三周周报
</span>
</a>
</div>

</nav>

	



</div>  
      <div class="openaside"><a class="navbutton" href="#" title="Show Sidebar"></a></div>

  <div id="toc" class="toc-aside">
  <strong class="toc-title">Contents</strong>
 
 <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-环境搭建"><span class="toc-number">1.</span> <span class="toc-text">1.环境搭建</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-1-操作系统准备"><span class="toc-number">1.1.</span> <span class="toc-text">1.1.操作系统准备</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-2-安装curl"><span class="toc-number">1.2.</span> <span class="toc-text">1.2.安装curl</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-3-安装docker"><span class="toc-number">1.3.</span> <span class="toc-text">1.3.安装docker</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-4-安装docker-compose"><span class="toc-number">1.4.</span> <span class="toc-text">1.4.安装docker-compose</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-5-安装Go"><span class="toc-number">1.5.</span> <span class="toc-text">1.5.安装Go</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-6-安装并检测git"><span class="toc-number">1.6.</span> <span class="toc-text">1.6.安装并检测git</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-7-下载fabric的引导脚本bootstrap-sh"><span class="toc-number">1.7.</span> <span class="toc-text">1.7.下载fabric的引导脚本bootstrap.sh</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-8-执行引导脚本bootstrap-sh"><span class="toc-number">1.8.</span> <span class="toc-text">1.8.执行引导脚本bootstrap.sh</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-9-添加环境变量"><span class="toc-number">1.9.</span> <span class="toc-text">1.9.添加环境变量</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-10-测试网络"><span class="toc-number">1.10.</span> <span class="toc-text">1.10.测试网络</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-11-错误处理"><span class="toc-number">1.11.</span> <span class="toc-text">1.11.错误处理</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-链码测试"><span class="toc-number">2.</span> <span class="toc-text">2.链码测试</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-什么是链码"><span class="toc-number">2.1.</span> <span class="toc-text">2.1.什么是链码</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-链码API"><span class="toc-number">2.2.</span> <span class="toc-text">2.2.链码API</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-简单资产链码"><span class="toc-number">2.3.</span> <span class="toc-text">2.3.简单资产链码</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-1-选择代码的位置"><span class="toc-number">2.3.1.</span> <span class="toc-text">2.3.1.选择代码的位置</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-2-准备工作"><span class="toc-number">2.3.2.</span> <span class="toc-text">2.3.2.准备工作</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-3-初始化链码"><span class="toc-number">2.3.3.</span> <span class="toc-text">2.3.3.初始化链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-4-调用链码"><span class="toc-number">2.3.4.</span> <span class="toc-text">2.3.4.调用链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-5-实现链码"><span class="toc-number">2.3.5.</span> <span class="toc-text">2.3.5.实现链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-6-集合代码"><span class="toc-number">2.3.6.</span> <span class="toc-text">2.3.6.集合代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-7-测试链码"><span class="toc-number">2.3.7.</span> <span class="toc-text">2.3.7.测试链码</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-7-1-终端一：开启网络"><span class="toc-number">2.3.7.1.</span> <span class="toc-text">2.3.7.1.终端一：开启网络</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-7-2-终端二：编译和运行链码"><span class="toc-number">2.3.7.2.</span> <span class="toc-text">2.3.7.2.终端二：编译和运行链码</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-7-3-终端三：使用链码"><span class="toc-number">2.3.7.3.</span> <span class="toc-text">2.3.7.3.终端三：使用链码</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-在first-network中测试链码"><span class="toc-number">3.</span> <span class="toc-text">3.在first-network中测试链码</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#3-1-术语介绍"><span class="toc-number">3.1.</span> <span class="toc-text">3.1.术语介绍</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-1-Channel"><span class="toc-number">3.1.1.</span> <span class="toc-text">3.1.1.Channel</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-2-Chaincode"><span class="toc-number">3.1.2.</span> <span class="toc-text">3.1.2.Chaincode</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-3-Ledger"><span class="toc-number">3.1.3.</span> <span class="toc-text">3.1.3.Ledger</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-4-network"><span class="toc-number">3.1.4.</span> <span class="toc-text">3.1.4.network</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-5-Fabric-CA"><span class="toc-number">3.1.5.</span> <span class="toc-text">3.1.5.Fabric CA</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-6-Peer"><span class="toc-number">3.1.6.</span> <span class="toc-text">3.1.6.Peer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-7-ordering-service"><span class="toc-number">3.1.7.</span> <span class="toc-text">3.1.7.ordering service</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-8-MSP"><span class="toc-number">3.1.8.</span> <span class="toc-text">3.1.8.MSP</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-2-链码开发"><span class="toc-number">3.2.</span> <span class="toc-text">3.2.链码开发</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-1-在GOPATH目录下git源码"><span class="toc-number">3.2.1.</span> <span class="toc-text">3.2.1.在GOPATH目录下git源码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-2-编写代码"><span class="toc-number">3.2.2.</span> <span class="toc-text">3.2.2.编写代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-3-拷贝链码到docker容器里面"><span class="toc-number">3.2.3.</span> <span class="toc-text">3.2.3.拷贝链码到docker容器里面</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-4-安装链码"><span class="toc-number">3.2.4.</span> <span class="toc-text">3.2.4.安装链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-5-实例化链码"><span class="toc-number">3.2.5.</span> <span class="toc-text">3.2.5.实例化链码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-6-调用链码"><span class="toc-number">3.2.6.</span> <span class="toc-text">3.2.6.调用链码</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#总结"><span class="toc-number">4.</span> <span class="toc-text">总结</span></a></li></ol>
 
  </div>

<div id="asidepart">
<div class="closeaside"><a class="closebutton" href="#" title="Hide Sidebar"></a></div>
<aside class="clearfix">

  

  
<div class="tagslist">
	<p class="asidetitle">Tags</p>
		<ul class="clearfix">
		
			
				<li><a href="/tags/周报/" title="周报">周报<sup>23</sup></a></li>
			
		
			
				<li><a href="/tags/谭俊熙/" title="谭俊熙">谭俊熙<sup>11</sup></a></li>
			
		
			
				<li><a href="/tags/苏思捷/" title="苏思捷">苏思捷<sup>5</sup></a></li>
			
		
			
				<li><a href="/tags/学习报告/" title="学习报告">学习报告<sup>5</sup></a></li>
			
		
			
				<li><a href="/tags/卢家豪/" title="卢家豪">卢家豪<sup>5</sup></a></li>
			
		
			
				<li><a href="/tags/报告/" title="报告">报告<sup>5</sup></a></li>
			
		
			
				<li><a href="/tags/苏佳鑫/" title="苏佳鑫">苏佳鑫<sup>4</sup></a></li>
			
		
			
				<li><a href="/tags/冯华文/" title="冯华文">冯华文<sup>4</sup></a></li>
			
		
			
				<li><a href="/tags/周报反馈/" title="周报反馈">周报反馈<sup>2</sup></a></li>
			
		
			
				<li><a href="/tags/吴朝捷/" title="吴朝捷">吴朝捷<sup>2</sup></a></li>
			
		
			
				<li><a href="/tags/黎宁/" title="黎宁">黎宁<sup>1</sup></a></li>
			
		
			
				<li><a href="/tags/笔记/" title="笔记">笔记<sup>1</sup></a></li>
			
		
		</ul>
</div>


</aside>
</div>
    </div>
    <footer><div id="footer" >
	
	
	<div class="social-font" class="clearfix">
		
		
		
		
		
		
		
		
		
		
	</div>
			
		

		<p class="copyright">
		
		</p>
</div>
</footer>
    <script src="/js/jquery-2.0.3.min.js"></script>
<script src="/js/jquery.imagesloaded.min.js"></script>
<script src="/js/gallery.js"></script>
<script src="/js/jquery.qrcode-0.12.0.min.js"></script>

<script type="text/javascript">
$(document).ready(function(){ 
  $('.navbar').click(function(){
    $('header nav').toggleClass('shownav');
  });
  var myWidth = 0;
  function getSize(){
    if( typeof( window.innerWidth ) == 'number' ) {
      myWidth = window.innerWidth;
    } else if( document.documentElement && document.documentElement.clientWidth) {
      myWidth = document.documentElement.clientWidth;
    };
  };
  var m = $('#main'),
      a = $('#asidepart'),
      c = $('.closeaside'),
      o = $('.openaside');
  c.click(function(){
    a.addClass('fadeOut').css('display', 'none');
    o.css('display', 'block').addClass('fadeIn');
    m.addClass('moveMain');
  });
  o.click(function(){
    o.css('display', 'none').removeClass('beforeFadeIn');
    a.css('display', 'block').removeClass('fadeOut').addClass('fadeIn');      
    m.removeClass('moveMain');
  });
  $(window).scroll(function(){
    o.css("top",Math.max(80,260-$(this).scrollTop()));
  });
  
  $(window).resize(function(){
    getSize(); 
    if (myWidth >= 1024) {
      $('header nav').removeClass('shownav');
    }else{
      m.removeClass('moveMain');
      a.css('display', 'block').removeClass('fadeOut');
      o.css('display', 'none');
      
      $('#toc.toc-aside').css('display', 'none');
        
    }
  });
});
</script>

<script type="text/javascript">
$(document).ready(function(){ 
  var ai = $('.article-content>iframe'),
      ae = $('.article-content>embed'),
      t  = $('#toc'),
      ta = $('#toc.toc-aside'),
      o  = $('.openaside'),
      c  = $('.closeaside');
  if(ai.length>0){
    ai.wrap('<div class="video-container" />');
  };
  if(ae.length>0){
   ae.wrap('<div class="video-container" />');
  };
  c.click(function(){
    ta.css('display', 'block').addClass('fadeIn');
  });
  o.click(function(){
    ta.css('display', 'none');
  });
  $(window).scroll(function(){
    ta.css("top",Math.max(140,320-$(this).scrollTop()));
  });
});
</script>


<script type="text/javascript">
$(document).ready(function(){ 
  var $this = $('.share'),
      url = $this.attr('data-url'),
      encodedUrl = encodeURIComponent(url),
      title = $this.attr('data-title'),
      tsina = $this.attr('data-tsina'),
      description = $this.attr('description');
  var html = [
  '<div class="hoverqrcode clearfix"></div>',
  '<a class="overlay" id="qrcode"></a>',
  '<a href="https://www.facebook.com/sharer.php?u=' + encodedUrl + '" class="article-share-facebook" target="_blank" title="Facebook"></a>',
  '<a href="https://twitter.com/intent/tweet?url=' + encodedUrl + '" class="article-share-twitter" target="_blank" title="Twitter"></a>',
  '<a href="#qrcode" class="article-share-qrcode" title="微信"></a>',
  '<a href="http://widget.renren.com/dialog/share?resourceUrl=' + encodedUrl + '&srcUrl=' + encodedUrl + '&title=' + title +'" class="article-share-renren" target="_blank" title="人人"></a>',
  '<a href="http://service.weibo.com/share/share.php?title='+title+'&url='+encodedUrl +'&ralateUid='+ tsina +'&searchPic=true&style=number' +'" class="article-share-weibo" target="_blank" title="微博"></a>',
  '<span title="Share to"></span>'
  ].join('');
  $this.append(html);

  $('.hoverqrcode').hide();

  var myWidth = 0;
  function updatehoverqrcode(){
    if( typeof( window.innerWidth ) == 'number' ) {
      myWidth = window.innerWidth;
    } else if( document.documentElement && document.documentElement.clientWidth) {
      myWidth = document.documentElement.clientWidth;
    };
    var qrsize = myWidth > 1024 ? 200:100;
    var options = {render: 'image', size: qrsize, fill: '#2ca6cb', text: url, radius: 0.5, quiet: 1};
    var p = $('.article-share-qrcode').position();
    $('.hoverqrcode').empty().css('width', qrsize).css('height', qrsize)
                          .css('left', p.left-qrsize/2+20).css('top', p.top-qrsize-10)
                          .qrcode(options);
  };
  $(window).resize(function(){
    $('.hoverqrcode').hide();
  });
  $('.article-share-qrcode').click(function(){
    updatehoverqrcode();
    $('.hoverqrcode').toggle();
  });
  $('.article-share-qrcode').hover(function(){}, function(){
      $('.hoverqrcode').hide();
  });
});   
</script>











<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" media="screen" type="text/css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script type="text/javascript">
$(document).ready(function(){ 
  $('.article-content').each(function(i){
    $(this).find('img').each(function(){
      if ($(this).parent().hasClass('fancybox')) return;
      var alt = this.alt;
      if (alt) $(this).after('<span class="caption">' + alt + '</span>');
      $(this).wrap('<a href="' + this.src + '" title="' + alt + '" class="fancybox"></a>');
    });
    $(this).find('.fancybox').each(function(){
      $(this).attr('rel', 'article' + i);
    });
  });
  if($.fancybox){
    $('.fancybox').fancybox();
  }
}); 
</script>



<!-- Analytics Begin -->





<!-- Analytics End -->

<!-- Totop Begin -->

	<div id="totop">
	<a title="Back to Top"><img src="/img/scrollup.png"/></a>
	</div>
	<script src="/js/totop.js"></script>

<!-- Totop End -->

<!-- MathJax Begin -->
<!-- mathjax config similar to math.stackexchange -->


<!-- MathJax End -->

<!-- Tiny_search Begin -->

<!-- Tiny_search End -->

  </body>
</html>
